#include "Cell.h"
#include "GameBoard.h"
#include <iostream>
#include <cstdlib>      //For the rand and srand functions, and for the system("cls") function
#include <ctime>        //For the time function
#include <windows.h>   // WinApi header

using std::cout;
using std::endl;
using std::cin;

/*************************************************************************************************
 *      The constructor for the gameboard takes a user input from main for the size of the board.*
 *      It then generates the board and assigns random numbers to each cell in the board by      *
 *              using the rand and srand and time functions.                                     *
 ************************************************************************************************/
GameBoard::GameBoard(int size, int gameType)
{
	setGameType(gameType);
	//Set the number of colors on the board to 0 for each color
	//The sum of all these color variables below equals the area of the board
	numR=0, numG=0, numB=0, numY=0, numP=0, numO=0;
	//Set the number of tries
	if(size == 12) { numberOfTries = 22; }	//small
	if(size == 17) { numberOfTries = 30; }	//medium
	if(size == 22) { numberOfTries = 36; }	//large
	currentTry = 0;


	SIZE = size;
        int count1 = 0, count2 = 0;     //The counters for generating the random numbers loop

        //Get the system time and seed the random number generator. We have to typecast to unsigned int to avoid an error with type time_t
		if(runs == 0)	//only if its the first run
			srand((unsigned int)time(0));
        
        _masterptr = new Cell*[size];   //Creates a new array of pointers to Cell objects
        for(int i=0; i<size; ++i)       //We have to create an array of pointers to an array of Cells to get this to work
        _masterptr[i]=new Cell[size];   //This syntax is confusing, but it allows us to acces the array now using _masterptr[][] notation.
                                        //I had to do this because you cannot create a new 2 dimensional array.
        

        //*******************************************************
        //Then we will populate the board with random numbers   *
        //*******************************************************
        for(count1 = 0; count1 < size; count1++)
        {
                for(count2 = 0; count2 < size; count2++)
                {
                    _masterptr[count1][count2].SetColor((rand() % 6));//This is generating a random number between 0 and 5

					addToColor(_masterptr[count1][count2].GetColor());		//This is adding one to the total amount of the color chosen
                }
        }

	_masterptr[0][0].setFlooded(true);
	_masterptr[0][0].setNextTo(true);	//These 3 functions are called so everything counts correctly.
	minusFromColor(_masterptr[0][0].GetColor());

	AssignNextTo();
	Flooder(_masterptr[0][0].getActualColor());
	hConsole = GetStdHandle(STD_OUTPUT_HANDLE);

}

//Destructor
GameBoard::~GameBoard()	//To delete the _masterptr and what it points to
		{
			for(int i=0; i<SIZE; i++)
			{
				delete _masterptr[i];
			}
			delete [] _masterptr;
		}


/********************************************************************************************************
 *      This function will display the board in full. It takes in the size of the board as an argument  *
 ********************************************************************************************************/
void GameBoard::DisplayBoard(int size)
{

    system("cls");          //Clears the screen
    int count1 = 0, count2 = 0;     //The counters for displaying the board
    cout << "\n\n\n";       //Format it into about the middle of the screen

    for(count1 = 0; count1 < size; count1++)        //Display the board one element at a time
    {
            cout << "\t\t"; //Format it into about the middle of the screen
            for(count2 = 0; count2 < size; count2++)
            {
				GetCellColor(count1, count2);

				//pick the colorattribute you want
				SetConsoleTextAttribute(hConsole, displayColor);

				cout << _masterptr[count1][count2].GetColor() << " ";
            }
            cout << endl;
    }
	SetConsoleTextAttribute(hConsole, 7);

}

//countColorNextToFlooded
//
//Function counts the number of cells next to each color touching the flooded region
//
//Pre:	Requires a color
//Post: counts the cells touching cells that are next to the flooded region, but that are not flooded or touching the
//		flooded region themselves,
//		will replace the current colorPriority if the number of cells is greater than what is currently in colorPriority
//
void GameBoard::countColorNextToFlooded(Color cc)
{
	colorPriorityCompare = 0;
	
	///////////////////////////////
	//Loop to go through array and set all counted bools to false
	//Put this at end instead?
	///////////////////////////////////
	for(int ii = 0; ii<SIZE; ii++)
	{
		for(int jj = 0; jj <SIZE; jj++)
		{
			_masterptr[ii][jj].setCounted(false);
		}
	}
	///////////////////////////////


	for(int ii = 0; ii<SIZE; ii++)
	{
		for(int jj = 0; jj <SIZE; jj++)
		{
			if(_masterptr[ii][jj].getActualColor() == cc && _masterptr[ii][jj].getNextTo() == true)
			{
				//check if above exists, is not flooded, is not next to flooded, and not counted
				if(ii != 0 &&										//cell is not on the top row
					_masterptr[ii-1][jj].getFlooded() == false && //cell above is not flooded
					_masterptr[ii-1][jj].getNextTo() == false && //cell above is not next to flooded
					_masterptr[ii-1][jj].getCounted() == false) //cell above has not been counted by this color
				{
					colorPriorityCompare++;
					_masterptr[ii-1][jj].setCounted(true);
				}

				//check if below exists, is not flooded, is not next to flooded, and not counted
				if(ii != SIZE-1 &&									//cell is not on the bottom row
					_masterptr[ii+1][jj].getFlooded() == false && //cell below is not flooded
					_masterptr[ii+1][jj].getNextTo() == false && //cell below is not next to flooded
					_masterptr[ii+1][jj].getCounted() == false)//cell below has not been counted by this color
				{
					colorPriorityCompare++;
					_masterptr[ii+1][jj].setCounted(true);
				}

				//check if left exists, is not flooded, is not next to flooded, and not counted
				if(jj != 0 &&										//cell is not at the beginning of the row
					_masterptr[ii][jj-1].getFlooded() == false && //cell to the right is not flooded
					_masterptr[ii][jj-1].getNextTo() == false && //cell to the right is not next to flooded
					_masterptr[ii][jj-1].getCounted() == false)//cell to the right has not been counted by this color
				{
					colorPriorityCompare++;
					_masterptr[ii][jj-1].setCounted(true);
				}

				//check if right exists, is not flooded, is not next to flooded, and not counted
				if(jj != SIZE && 
					_masterptr[ii][jj+1].getFlooded() == false && 
					_masterptr[ii][jj+1].getNextTo() == false && 
					_masterptr[ii][jj+1].getCounted() == false)
				{
					colorPriorityCompare++;
					_masterptr[ii][jj+1].setCounted(true);
				}
			}
		}
	}

	
	//this compares the cell count of the current color to the color with the highest cell count up till now 
	if(colorPriority.first < colorPriorityCompare)
	{
		colorPriority.first = colorPriorityCompare;
		colorPriority.second = cc;
	}

	
}

//AssignNextTo
//
//Function that finds a cell touching the flooded region and calls the VeinOfNextTo function, which will change the NextTo bool
//of the cell and any vein that it is part of.
//
//Pre:  None
//Post: Calls VeinOfNextTo until all cells and veins touching the flooded region are assigned as next to the flooded region
//		
//
void GameBoard::AssignNextTo()
{

	for(int ii = 0; ii<SIZE; ii++)
	{
		for(int jj = 0; jj <SIZE; jj++)
		{
			if(_masterptr[ii][jj].getFlooded() == true)
			{
				//check if above exists and if it is not flooded
				if(ii != 0 && _masterptr[ii-1][jj].getFlooded() != true)
				{
					VeinOfNextTo(ii-1, jj);
				}

				//check if below exists and if it is flooded
				if(ii != SIZE-1 && _masterptr[ii+1][jj].getFlooded() != true)
				{
					VeinOfNextTo(ii+1, jj);
				}

				//check if left exists and if it is flooded
				if(jj != 0 && _masterptr[ii][jj-1].getFlooded() != true)
				{
					VeinOfNextTo(ii, jj-1);
				}

				//check if right exists and is flooded
				if(jj != SIZE-1 && _masterptr[ii][jj+1].getFlooded() != true)
				{
					VeinOfNextTo(ii, jj+1);
				}
			}
		}
	}

}

//Flooder
//
//Function that finds a cell marked as flooded and changes its color to the color choice of the current turn.
//
//Pre:  Requires a color
//Post: All cells with the flooded bool set to true are changed to the color choice of the current turn
//		
//
void GameBoard::Flooder(Color cc)
{

	for(int ii = 0; ii<SIZE; ii++)
	{
		for(int jj = 0; jj <SIZE; jj++)
		{
			if(_masterptr[ii][jj].getFlooded() == true)
			{
				_masterptr[ii][jj].SetColor(cc);
			}

			else if(_masterptr[ii][jj].getNextTo() == true && _masterptr[ii][jj].getActualColor() == cc)
			{
				_masterptr[ii][jj].setFlooded(true);
			}
		}
	}

	AssignNextTo();
}

//VeinOfNextTo
//
//Function that recursively finds veins of cells that are the same color and sets their NextTo bool to true
//
//Pre:  Requires two integers(the coordinates of the cell on the board)
//Post: All cells of a vein touching the flooded region have their NextTo bool set to true
//		
//
void GameBoard::VeinOfNextTo(int ii, int jj)
{
	if(_masterptr[ii][jj].getNextTo() != true)	//If its not marked as next to already, then mark it and minus it from total colors
	{
		minusFromColor(_masterptr[ii][jj].GetColor());
	}
	_masterptr[ii][jj].setNextTo(true);


	//if cell above exists, is the same color, and is not marked as NextTo
	if(ii!= 0 && _masterptr[ii-1][jj].getActualColor() == _masterptr[ii][jj].getActualColor() &&
		_masterptr[ii-1][jj].getNextTo() != true)
	{
		VeinOfNextTo(ii-1, jj);
	}

	//if cell right exists, is the same color, and is not marked as NextTo
	if(jj != SIZE-1 && _masterptr[ii][jj+1].getActualColor() == _masterptr[ii][jj].getActualColor() && 
		_masterptr[ii][jj+1].getNextTo() != true)
	{
		VeinOfNextTo(ii, jj+1);
	}

	//if cell below exists, is the same color, and is not marked as NextTo
	if(ii != SIZE-1 && _masterptr[ii+1][jj].GetColor() == _masterptr[ii][jj].GetColor() && 
		_masterptr[ii+1][jj].getNextTo() != true)
	{
		VeinOfNextTo(ii+1, jj);
	}

	//if cell left exists, is the same color, and is not marked as NextTo
	if(jj != 0 && _masterptr[ii][jj-1].GetColor() == _masterptr[ii][jj].GetColor() && 
		_masterptr[ii][jj-1].getNextTo() != true)
	{
		VeinOfNextTo(ii, jj-1);
	}

}

//ColorCycle
//
//This function calls the countColorNextToFlooded for the colors each turn and gets the count of how many cells or
//veins of cells that the flooded region is not touching
//
//Pre:  None
//
//Post: No Return
//		Will find the priority of each color on the board
//		
void GameBoard::ColorCycle()
{	
	colorPriority.first = 0;
	countColorNextToFlooded(Red);
	countColorNextToFlooded(Green);
	countColorNextToFlooded(Blue);
	countColorNextToFlooded(Yellow);
	countColorNextToFlooded(Purple);
	countColorNextToFlooded(Orange);

}

//getPriority
//
//This function returns the value of the highest priority color found so far
//
//Pre:  None
//
//Post: Returns an int
//
int GameBoard::getPriority()
{
	return colorPriority.first;
}



void GameBoard::GetCellColor(int ii, int jj)
{
	if(_masterptr[ii][jj].GetColor() == 'R')
		displayColor = 12;

	else if(_masterptr[ii][jj].GetColor() == 'G')
		displayColor = 2;

	else if(_masterptr[ii][jj].GetColor() == 'B')
		displayColor = 9;

	else if(_masterptr[ii][jj].GetColor() == 'Y')
		displayColor = 14;

	else if(_masterptr[ii][jj].GetColor() == 'P')
		displayColor = 5;

	else if(_masterptr[ii][jj].GetColor() == 'O')
		displayColor = 13;
	
	else
		displayColor = 2;
	
}


bool GameBoard::GameEnd()
{
	for(int ii = 0; ii<SIZE; ii++)
	{
		for(int jj = 0; jj <SIZE; jj++)
		{
			if(_masterptr[ii][jj].getFlooded() != true)
			{
				return false;
			}
		}
	}
	return true;
}

Color GameBoard::getMasterColor()
{ 
	return _masterptr[0][0].getActualColor();
}

void GameBoard::GameDriver()
{
	while(GameEnd() == false)
	{
		checkForElimination();	//Checks to see if a color can be eliminated completely

		if(gameType != 3)
		{
			DisplayBoard(SIZE);
			cout << endl << "Current try: " << currentTry << "/" << numberOfTries << endl;
			if(gameType != 1)
				{	
				cout << "Next color switch: ";
			
				if(colorPriority.second == Red) { SetConsoleTextAttribute(hConsole, 12); cout << "Red"; }
				if(colorPriority.second == Green) { SetConsoleTextAttribute(hConsole, 2); cout << "Green"; }
				if(colorPriority.second == Blue) { SetConsoleTextAttribute(hConsole, 9); cout << "Blue"; }
				if(colorPriority.second == Yellow) { SetConsoleTextAttribute(hConsole, 14); cout << "Yellow"; }
				if(colorPriority.second == Purple) { SetConsoleTextAttribute(hConsole, 5); cout << "Purple"; }
				if(colorPriority.second == Orange) { SetConsoleTextAttribute(hConsole, 13); cout << "Orange"; }
				cin.sync();
				cin.get();
				}
		}
		if(gameType == 1)
		{
			bool userChoiceOfColor = false;
			do
			{
			cout << "\nPick a color (r,g,b,y,p,o)\n>>";
			cin >> userColorChoice;
			colorPriority.first = 100;

			switch(toupper(userColorChoice))
			{
				case 'R': colorPriority.second = Red; userChoiceOfColor = true; break; 
				case 'G': colorPriority.second = Green; userChoiceOfColor = true; break;
				case 'B': colorPriority.second = Blue; userChoiceOfColor = true; break;
				case 'Y': colorPriority.second = Yellow; userChoiceOfColor = true; break;
				case 'P': colorPriority.second = Purple; userChoiceOfColor = true; break;
				case 'O': 
				case '0': colorPriority.second = Orange; userChoiceOfColor = true; break;

				default: cout << "Please choose a proper color.\n";
			}

			}while(userChoiceOfColor == false);
		}

		Flooder(colorPriority.second);
		++currentTry;
	}

	if(GameEnd() == true)
	{
		++runs;

		if(gameType != 3)
			DisplayBoard(SIZE);

			if(currentTry <= numberOfTries)
			{
				++wins;
				if(gameType !=3 )
				{
					cout << endl << "\t      ___                       ___     " << endl
						<<"\t     /\\  \\                     /\\  \\    " << endl
						<<"\t    _\\:\\  \\       ___          \\:\\  \\   " << endl
						<<"\t   /\\ \\:\\  \\     /\\__\\          \\:\\  \\  " << endl
						<<"\t  _\\:\\ \\:\\  \\   /:/__/      _____\\:\\  \\ " << endl
						<<"\t /\\ \\:\\ \\:\\__\\ /::\\  \\     /::::::::\\__\\ " << endl
						<<"\t \\:\\ \\:\\/:/  / \\/\\:\\  \\__  \\:\\~~\\~~\\/__/" << endl
						<<"\t  \\:\\ \\::/  /   ~~\\:\\/\\__\\  \\:\\  \\      " << endl
						<<"\t   \\:\\/:/  /       \\::/  /   \\:\\  \\     " << endl
						<<"\t    \\::/  /        /:/  /     \\:\\__\\    " << endl
						<<"\t     \\/__/         \\/__/       \\/__/ " << endl;

					cout << endl << endl << "\tCongratulations! You won with " << numberOfTries-currentTry;
					(numberOfTries-currentTry == 1) ? ( cout << " try left!" ) : ( cout << " tries left!" );
				}
		
		}
		else
		{
			if(gameType != 3)
			{
				cout << endl << "I'm sorry, you were over the limit by " << abs(numberOfTries-currentTry);
				(abs(numberOfTries-currentTry) == 1) ? ( cout << " try!" ) : ( cout << " tries!" );
			}
		}
		cout << endl << endl << "You've won " << 100*(double(wins)/runs) << "% of your games played.\tRuns: " << runs << " \tWins: " << wins;
	}
}

void GameBoard::addToColor(char color)
{
	switch(color)
	{
		case 'R': ++numR; break;
		case 'G': ++numG; break;	
		case 'B': ++numB; break;	
		case 'Y': ++numY; break;	
		case 'P': ++numP; break;
		case 'O': ++numO; break;
	}
}

void GameBoard::minusFromColor(char color)
{
	switch(color)
	{
		case 'R': --numR; break;
		case 'G': --numG; break;
		case 'B': --numB; break;
		case 'Y': --numY; break;
		case 'P': --numP; break;
		case 'O': --numO; break;
	}
}

void GameBoard::checkForElimination()
{
	if(numR == 0)		{ colorPriority.first = 100; --numR; colorPriority.second = Red; }
	else if(numG == 0)	{ colorPriority.first = 100; --numG; colorPriority.second = Green; }
	else if(numB == 0)	{ colorPriority.first = 100; --numB; colorPriority.second = Blue; }
	else if(numY == 0)	{ colorPriority.first = 100; --numY; colorPriority.second = Yellow; }
	else if(numP == 0)	{ colorPriority.first = 100; --numP; colorPriority.second = Purple; }
	else if(numO == 0)	{ colorPriority.first = 100; --numO; colorPriority.second = Orange; }
	else { ColorCycle(); }
}